cJSON 사용법

개요

cJSON은 c에서 JSON을 다루기 위한 라이브러리입니다. 해당 라이브러리가 좋은 점은 단일 파일과 단일 헤더로 이루어져 있어서 importing이 매우 간단하다는 점입니다.

해당 파일은 여기에서 다운받을 수 있습니다.

해당 라이브러리는 MIT 라이센스로 누구나 자유롭게 사용 가능합니다.

설치

cmake를 통해서도 설치가 가능합니다. 하지만 라이브러리가 단일 파일로 되어있으므로 프로젝트에 직접 넣는 것이 더 간편합니다.

사이트에서 다운받은 파일 중 cJSON.ccJSON.h를 직접 프로젝트에 넣어주면 됩니다.

추후 #include "cjson/cJSON.h"와 같은 형식으로 include해주면 됩니다.

데이터 구조

CJSON은 CJSON struct 데이터 유형을 사용하여 JSON 데이터를 나타냅니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
/* The cJSON structure: */
typedef struct cJSON
{
struct cJSON *next;
struct cJSON *prev;
struct cJSON *child;
int type;
char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint;
double valuedouble;
char *string;
} cJSON;

이 유형의 항목은 JSON 값을 나타냅니다. 유형은type에 저장됩니다. 비트 플래그로 저장됩니다 (** 이것은type **의 값을 비교하여 유형을 찾을 수 없음을 의미합니다).

항목의 유형을 확인하려면 해당cjson_is ...기능을 사용하십시오. null 검사가 뒤 따른 다음 항목 이이 유형의 항목 인 경우 부울 값을 반환합니다.

유형은 다음 중 하나 일 수 있습니다.

  • cjson_invalid (cjson_isinvalid으로 확인) : 값이없는 유효하지 않은 항목을 나타냅니다. 항목을 모두 0 바이트로 설정하면 자동 으로이 유형이 있습니다.
  • cjson_false (cjson_isfalse에 체크) :false부울 값을 나타냅니다. cjson_isboool로 Boolean 값을 확인할 수도 있습니다.
  • cjson_true (cjson_istreue로 확인) :true Boolean 값을 나타냅니다. cjson_isboool로 Boolean 값을 확인할 수도 있습니다.
  • cjson_null (cjson_isnull로 확인) :null 값을 나타냅니다.
  • cjson_number (cjson_isnumber와 함께 확인) : 숫자 값을 나타냅니다. 값은ValueDouble의 두 배로 저장되고Validint에도 저장됩니다. 숫자가 정수 범위를 벗어나면int_max 또는int_minValidint에 사용된다.
  • CJSON_String (cjson_isstring에 확인) : 문자열 값을 나타냅니다. valuestring의 0 종단 문자열 형태로 저장됩니다.
  • cjson_array (cjson_isarray로 확인) : 배열 값을 나타냅니다. 이는자식을 배열의 값을 나타내는cjson 항목의 링크 된 목록으로 가리키는 것으로 구현됩니다. 요소는nextprev를 사용하여pref.next == null와 마지막 요소가null == null'를 갖는nextprev`를 사용하여 링크됩니다.
  • cjson_object (cjson_isobject에 확인) : 객체 값을 나타냅니다. 객체는 배열과 동일한 방식으로 저장되며, 유일한 차이점은 객체의 항목이string의 키를 저장한다는 것입니다.
  • CJSON_RAW (cjson_israw에 체크) :valuestring의 문자의 제로 종료 배열로 저장된 모든 종류의 JSON을 나타냅니다. 예를 들어, 성능을 절약하기 위해 동일한 정적 JSON을 반복해서 반복해서 인쇄하지 않도록 사용할 수 있습니다. cjson은 구문 분석 할 때이 유형을 만들지 않습니다. 또한 CJSON은 유효한 JSON인지 확인하지 않습니다.

또한 다음 두 가지 플래그가 있습니다.

  • cjson_isreference :자녀가 가리키는 항목과 / 또는valuestring항목 이이 항목에 의해 소유되지 않는 항목을 지정하여 참조 일뿐입니다. 따라서 CJSON_DELETE 및 다른 기능은vehild /valuestring이 아닌이 항목 만 할당 해제합니다.
  • cjson_stringisconst : 이것은string가 일정한 문자열을 가리킨다는 것을 의미합니다. 즉,cjson_delete 및 다른 함수는string을 할당 해제하려고하지 않는다는 것을 의미합니다.

데이터 구조로 작동합니다

모든 값 유형에 대해 해당 유형의 항목을 만드는 데 사용할 수있는cjson_create ...함수가 있습니다.

이 모든 것은 나중에cjson_delete로 삭제 될 수있는cjson struct를 할당 할 것입니다.

어떤 시점에서 삭제해야합니다. 그렇지 않으면 메모리 누수가 발생합니다.

중요 : 배열이나 오브젝트에 항목을 추가 한 경우 cjson_delete로 삭제해서는 안됩니다. 배열이나 오브젝트에 추가하면 소유권을 전송하여 해당 배열이나 개체가 삭제되면 삭제됩니다.

cjson_setvalueString을 사용하여cjson_string의’valuestring을 변경하고 이전valuestring`를 수동으로 해제 할 필요는 없습니다.

기본 유형

  • NULLcjson_createnull로 생성됩니다
  • booleanscjson_creaTeetRue,cjson_createfalse 또는cjson_createbool으로 생성된다
  • numberscjson_createNumber로 생성됩니다. 이것은ValueDoubleValueInt 둘 다 설정됩니다. 숫자가 정수 범위를 벗어나면int_max 또는int_minValidint에 사용된다.
  • stringscjson_createString (문자열 복사) 또는cjson_createStringReference (직접 문자열을 가리키는 것을 의미합니다. valuestringcjson_delete에 의해 삭제되지 않고 수명, 상수에 유용합니다.)

Arrays

cjson_createarray가있는 빈 배열을 만들 수 있습니다. CJSON_CreateArrayRayference는 콘텐츠를 “소유”하지 않는 배열을 만드는 데 사용할 수 있으므로 콘텐츠가cjson_delete에 의해 삭제되지 않습니다.

항목을 배열에 추가하려면cjson_additemtoarray를 사용하여 항목을 끝까지 추가합니다.
cjson_additemreferencetoarray를 사용하여 요소는 다른 항목, 배열 또는 문자열에 대한 참조로 추가 할 수 있습니다. 즉,CJSON_DELETE자식또는VALUESTRING'속성을 삭제하지 않으므로 이미 다른 곳에서 이미 사용되는 경우 이중 해제가 발생하지 않습니다. 중간에 항목을 삽입하려면cjson_insertiteminarray`를 사용하십시오. 주어진 0 기반 인덱스에 항목을 삽입하고 기존 항목을 오른쪽으로 이동합니다.

주어진 인덱스에서 배열에서 항목을 꺼내고 계속 사용하려면cjson_detachitemfromarray를 사용하여 분리 된 항목을 반환하므로 포인터에 할당해야합니다. 그렇지 않으면 메모리 누수가 발생합니다.

삭제 항목은cjson_deleteItemFromArray로 수행됩니다. 그것은cjson_detachitemfromarray와 같이 작동하지만cjson_delete를 통해 분리 된 항목을 삭제합니다.

배열의 항목을 대체 할 수도 있습니다. 색인을 사용하여cjson_replaceItemInarray 또는cjson_replaceItemViApointercjson_replaceItemViApointer'를 사용하여cjson_replaceItemViApointercjson_replaceItemViaPointer가 실패하면0`을 반환합니다. 이게 내부적으로 오래된 항목을 분리하고 삭제하고 새 항목을 그 자리에 삽입하는 것입니다.

배열의 크기를 얻으려면cjson_getArraysize를 사용하십시오. cjson_getArrayItem를 사용하여 주어진 인덱스에서 요소를 구할 수 있습니다.

배열은 링크 된 목록으로 저장되므로 인덱스를 통해 반복하는 것은 비효율적입니다 (o (n²))cojson_arrayforeach 매크로를 사용하여 배열을 반복 할 수 있습니다.

object

cjson_createobject가있는 빈 객체를 만들 수 있습니다. CJSON_CreateObjectReference는 콘텐츠를 “소유”하지 않는 개체를 만드는 데 사용할 수 있으므로 콘텐츠가cjson_delete에 의해 삭제되지 않습니다.

객체에 항목을 추가하려면cjson_additemtoobject를 사용하십시오. cjson_additemtoobjectcs를 사용하여 상수 또는 참조 인 이름을 가진 객체에 항목을 추가하여cjson_delete에 의해 해제되지 않도록 아이템 (cjson struct)의 키,string).

cjson_additemreferencetoarray를 사용하여 요소는 다른 객체, 배열 또는 문자열에 대한 참조로 추가 할 수 있습니다. 즉,CJSON_DELETE자식또는VALUESTRING속성을 삭제하지 않으므로 이미 다른 곳에서 이미 사용되는 경우 이중 해제가 발생하지 않습니다.

객체에서 항목을 꺼내려면cjson_detachitemfromobobjectcasesensitive를 사용하여 분리 된 항목을 반환하므로 포인터에 할당해야합니다. 그렇지 않으면 메모리 누수가 발생합니다.

항목을 삭제하면cjson_deleteItemFromObjectCasEnitive로 수행됩니다. 그것은cjson_detachitemfromobobjectcasesensitive와 같은 일이 뒤 따른다.

객체의 항목을 제자리에서 바꿀 수도 있습니다. cjson_replaceItemInoBjectCasEnitive 키를 사용하여cjson_replaceItemViApointer를 사용하여cjson_replaceItemViApointer cjson_replaceItemViaPointer가 실패하면0을 반환합니다. 이게 내부적으로 오래된 항목을 분리하고 삭제하고 새 항목을 그 자리에 삽입하는 것입니다.

객체의 크기를 가져 오려면cjson_getArraysize, 내부 오브젝트가 배열로 저장되기 때문에 작동합니다.

객체의 항목에 액세스하려면cjson_getobjectItemCasensitive를 사용하십시오.

객체를 반복하려면 배열과 동일한 방식으로cjson_arrayforeach 매크로를 사용할 수 있습니다.

CJSON은 또한 새로운 항목을 빠르게 만들고cjson_addnulltoobject와 같은 객체에 추가하기위한 편리한 도우미 기능을 제공합니다. 그들은 실패한 경우 새 항목이나null에 대한 포인터를 반환합니다.

JSON 파싱

JSON이 0 종료 문자열에 주어지면cjson_parse로 파싱 할 수 있습니다.

1
cJSON *json = cJSON_Parse(string);

일부 JSON은 문자열에 (0을 종료했는지 여부)cjson_parsewithLength로 구문 분석 할 수 있습니다.

1
cJSON *json = cJSON_ParseWithLength(string, buffer_length);

그것은 JSON을 구문 분석하고CJSON 항목의 tree를 할당 할 것입니다. 반환되면cjson_delete와 함께 사용한 후에 그것을 할당 해제 할 책임이 있습니다.

cjson_parse에서 사용하는 할당자는mallocfree이지만,cjson_inithooks으로 변경 될 수 있습니다 (전역으로).

오류가 발생하면cjson_geterrorptr를 사용하여 입력 문자열의 오류 위치에 대한 포인터를 액세스 할 수 있습니다. 주는이 경우 멀티 스레딩 시나리오에서 경쟁 조건을 생성 할 수 있지만이 경우return_parse_endcjson_parsewithopts를 사용하는 것이 좋습니다.
기본적으로 파싱 된 JSON을 따르는 입력 문자열의 문자는 오류로 간주되지 않습니다.

더 많은 옵션을 원한다면cjson_parsewithopts (const char *value, const char **return_parse_end, cjson_bool requal_null_terminated)를 사용하십시오.
return_parse_end는 입력 문자열의 JSON 끝이나 오류가 발생한 위치에있는 포인터를 반환합니다 (cjson_geterrorptr을 스레드 안전 방식으로 대체 함). 1으로 설정하면 입력 문자열에 JSON 이후의 데이터가 포함되어 있으면 오류가 발생합니다.

버퍼 길이를 제공하는 옵션을 더 원하면cjson_parsewithlengthOpts (const char *value, size_t buffer_length, const char **return_parse_end, cjson_bool requy_null_terminated)를 사용하십시오.

JSON printing

cjson 항목의 tree가 주어지면cjson_print를 사용하여 문자열로 인쇄 할 수 있습니다.

1
char *string = cJSON_Print(json);

문자열을 할당하고 트리의 JSON 표현을 인쇄합니다. 일단 반환되면 할당 자와 함께 사용한 후에 할당 해제 할 책임이 있습니다. (일반적으로무료'는cjson_inithooks`로 설정된 것에 달려 있습니다.)

cjson_print는 포맷을 위해 공백으로 인쇄됩니다. 서식을 지정하지 않고 인쇄하려면cjson_printUnformatted를 사용하십시오.

결과 문자열이 얼마나 큰지에 대한 대략적인 아이디어가있는 경우cjson_printbuffered (const cjson * item, int prebuffer, cjson_bool fmt)를 사용할 수 있습니다. fmt는 공백을 켜고 끌 수있는 포맷을 켜는 부울입니다. Prebuffer는 인쇄에 사용할 첫 번째 버퍼 크기를 지정합니다. cjson_print는 현재 첫 번째 버퍼 크기를 위해 256 바이트를 사용합니다. 인쇄가 공간이 부족하면 새 버퍼가 할당되고 인쇄가 계속되기 전에 이전 버퍼가 복사됩니다.

이러한 동적 버퍼 할당은cjson_printpreallocated (cjson * 항목, char * 버퍼, const int length, const cjson_bool 형식)를 사용하여 완전히 피할 수 있습니다. 인쇄 할 포인터와 그 길이의 버퍼가 필요합니다. 길이에 도달하면 인쇄가 실패하고0을 반환합니다. 성공의 경우,1가 반환됩니다. CJSON은 제공된 메모리가 충분할 경우 추정 할 때 100 % 정확하지 않기 때문에 실제로 5 바이트를 더 많이 제공해야합니다.

예졔

이 예에서는 다음 JSON을 구축하고 구문 분석하려고합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"name": "Awesome 4K",
"resolutions": [
{
"width": 1280,
"height": 720
},
{
"width": 1920,
"height": 1080
},
{
"width": 3840,
"height": 2160
}
]
}

printing

위의 JSON을 작성하고 문자열로 printing합시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//create a monitor with a list of supported resolutions
//NOTE: Returns a heap allocated string, you are required to free it after use.
char *create_monitor(void)
{
const unsigned int resolution_numbers[3][2] = {
{1280, 720},
{1920, 1080},
{3840, 2160}
};
char *string = NULL;
cJSON *name = NULL;
cJSON *resolutions = NULL;
cJSON *resolution = NULL;
cJSON *width = NULL;
cJSON *height = NULL;
size_t index = 0;

cJSON *monitor = cJSON_CreateObject();
if (monitor == NULL)
{
goto end;
}

name = cJSON_CreateString("Awesome 4K");
if (name == NULL)
{
goto end;
}
/* after creation was successful, immediately add it to the monitor,
* thereby transferring ownership of the pointer to it */
cJSON_AddItemToObject(monitor, "name", name);

resolutions = cJSON_CreateArray();
if (resolutions == NULL)
{
goto end;
}
cJSON_AddItemToObject(monitor, "resolutions", resolutions);

for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index)
{
resolution = cJSON_CreateObject();
if (resolution == NULL)
{
goto end;
}
cJSON_AddItemToArray(resolutions, resolution);

width = cJSON_CreateNumber(resolution_numbers[index][0]);
if (width == NULL)
{
goto end;
}
cJSON_AddItemToObject(resolution, "width", width);

height = cJSON_CreateNumber(resolution_numbers[index][1]);
if (height == NULL)
{
goto end;
}
cJSON_AddItemToObject(resolution, "height", height);
}

string = cJSON_Print(monitor);
if (string == NULL)
{
fprintf(stderr, "Failed to print monitor.\n");
}

end:
cJSON_Delete(monitor);
return string;
}

또는 우리는 cJSON_Add...ToObject 도우미 기능을 사용하여 조금 더 쉽게 만들 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//NOTE: Returns a heap allocated string, you are required to free it after use.
char *create_monitor_with_helpers(void)
{
const unsigned int resolution_numbers[3][2] = {
{1280, 720},
{1920, 1080},
{3840, 2160}
};
char *string = NULL;
cJSON *resolutions = NULL;
size_t index = 0;

cJSON *monitor = cJSON_CreateObject();

if (cJSON_AddStringToObject(monitor, "name", "Awesome 4K") == NULL)
{
goto end;
}

resolutions = cJSON_AddArrayToObject(monitor, "resolutions");
if (resolutions == NULL)
{
goto end;
}

for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index)
{
cJSON *resolution = cJSON_CreateObject();

if (cJSON_AddNumberToObject(resolution, "width", resolution_numbers[index][0]) == NULL)
{
goto end;
}

if (cJSON_AddNumberToObject(resolution, "height", resolution_numbers[index][1]) == NULL)
{
goto end;
}

cJSON_AddItemToArray(resolutions, resolution);
}

string = cJSON_Print(monitor);
if (string == NULL)
{
fprintf(stderr, "Failed to print monitor.\n");
}

end:
cJSON_Delete(monitor);
return string;
}

Parsing

이 예제에서는 위의 형식으로 JSON을 구문 분석하고 일부 진단 출력을 인쇄하는 동안 모니터가 전체 HD 해상도를 지원하는지 확인합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/* return 1 if the monitor supports full hd, 0 otherwise */
int supports_full_hd(const char * const monitor)
{
const cJSON *resolution = NULL;
const cJSON *resolutions = NULL;
const cJSON *name = NULL;
int status = 0;
cJSON *monitor_json = cJSON_Parse(monitor);
if (monitor_json == NULL)
{
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL)
{
fprintf(stderr, "Error before: %s\n", error_ptr);
}
status = 0;
goto end;
}

name = cJSON_GetObjectItemCaseSensitive(monitor_json, "name");
if (cJSON_IsString(name) && (name->valuestring != NULL))
{
printf("Checking monitor \"%s\"\n", name->valuestring);
}

resolutions = cJSON_GetObjectItemCaseSensitive(monitor_json, "resolutions");
cJSON_ArrayForEach(resolution, resolutions)
{
cJSON *width = cJSON_GetObjectItemCaseSensitive(resolution, "width");
cJSON *height = cJSON_GetObjectItemCaseSensitive(resolution, "height");

if (!cJSON_IsNumber(width) || !cJSON_IsNumber(height))
{
status = 0;
goto end;
}

if ((width->valuedouble == 1920) && (height->valuedouble == 1080))
{
status = 1;
goto end;
}
}

end:
cJSON_Delete(monitor_json);
return status;
}

cjson_getobjectItemcasensitive가 이미null 입력에 대한 검사가null값이 전파되고cjson_isnumberchjson_isstring 0이면cjson_getobjectItemcasensitivecjson_parse의 결과를 제외하고는 null 검사가 없다는 것을 유의하십시오. 입력은null입니다.

공유하기